/******************************************************************************
 * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************/
/**	@page UART
 *
 *	Introduction
 *	===============
 *	B91 supports two uart: uart0~ uart1.
 *
 *	API Reference
 *	===============
 *	Header File: uart.h
 */
#ifndef UART_H_
#define UART_H_

#include "dma.h"
#include "gpio.h"
#include "reg_include/register_b91.h"
#include "timer.h"

extern unsigned char uart_rx_byte_index[2];
extern unsigned char uart_tx_byte_index[2];

#define uart_rtx_pin_tx_trig(uart_num) uart_clr_tx_done(uart_num)

/**********************************************************************************************************************
 *                                         global constants                                                           *
 *********************************************************************************************************************/

/**********************************************************************************************************************
 *                                           global macro                                                             *
 *********************************************************************************************************************/

/**********************************************************************************************************************
 *                                         global data type                                                           *
 *********************************************************************************************************************/
/**
 *  @brief  Define parity type
 */
typedef enum {
    UART_PARITY_NONE = 0,
    UART_PARITY_EVEN,
    UART_PARITY_ODD,
} uart_parity_e;

/**
 *  @brief  Define UART chn
 */
typedef enum {
    UART0 = 0,
    UART1,
} uart_num_e;

/**
 *  @brief  Define mul bits
 */
typedef enum {
    UART_BW_MUL1 = 0,
    UART_BW_MUL2 = 1,
    UART_BW_MUL3 = 2,
    UART_BW_MUL4 = 3,
} uart_timeout_mul_e;

/**
 *  @brief  Define the length of stop bit
 */
typedef enum {
    UART_STOP_BIT_ONE = 0,
    UART_STOP_BIT_ONE_DOT_FIVE = BIT(4),
    UART_STOP_BIT_TWO = BIT(5),
} uart_stop_bit_e;

/**
 *  @brief  Define UART RTS mode
 */
typedef enum {
    UART_RTS_MODE_AUTO = 0,
    UART_RTS_MODE_MANUAL,
} uart_rts_mode_e;

/**
 *  @brief  Define UART CTS pin : UART0(PA1 PB6 PD0), UART1(PC4 PD4 PE1)
 */
typedef enum {
    UART0_CTS_PA1 = GPIO_PA1,
    UART0_CTS_PB6 = GPIO_PB6,
    UART0_CTS_PD0 = GPIO_PD0,

    UART1_CTS_PC4 = GPIO_PC4,
    UART1_CTS_PD4 = GPIO_PD4,
    UART1_CTS_PE1 = GPIO_PE1,
} uart_cts_pin_e;

/**
 *  @brief  Define UART RTS pin : UART0(PA2 PB4 PD1), UART1(PC5 PD5 PE3)
 */
typedef enum {
    UART0_RTS_PA2 = GPIO_PA2,
    UART0_RTS_PB4 = GPIO_PB4,
    UART0_RTS_PD1 = GPIO_PD1,

    UART1_RTS_PC5 = GPIO_PC5,
    UART1_RTS_PD5 = GPIO_PD5,
    UART1_RTS_PE3 = GPIO_PE3,
} uart_rts_pin_e;

/**
 *  @brief  Define UART TX pin : UART0(PA3 PB2 PD2), UART1(PC6 PD6 PE0)
 */
typedef enum {
    UART0_TX_PA3 = GPIO_PA3,
    UART0_TX_PB2 = GPIO_PB2,
    UART0_TX_PD2 = GPIO_PD2,

    UART1_TX_PC6 = GPIO_PC6,
    UART1_TX_PD6 = GPIO_PD6,
    UART1_TX_PE0 = GPIO_PE0,

    UART_TX_ENUM_SIZE
} uart_tx_pin_e;

/**
 *  @brief  Define UART RX pin : UART0(PA4 PB3 PD3), UART1(PC7 PD7 PE2)
 */
typedef enum {
    UART0_RX_PA4 = GPIO_PA4,
    UART0_RX_PB3 = GPIO_PB3,
    UART0_RX_PD3 = GPIO_PD3,

    UART1_RX_PC7 = GPIO_PC7,
    UART1_RX_PD7 = GPIO_PD7,
    UART1_RX_PE2 = GPIO_PE2,

    UART_RX_ENUM_SIZE
} uart_rx_pin_e;

/**
 *  @brief  Define UART IRQ MASK.The enumeration variable is just a index,
 *          and actually needs to be operated registers behind.
 */
typedef enum {
    UART_RX_IRQ_MASK = BIT(0),   // reg_uart_ctrl0(uart_num)       BIT(6)
    UART_TX_IRQ_MASK = BIT(1),   // reg_uart_ctrl0(uart_num)       BIT(7)
    UART_RXDONE_MASK = BIT(2),   // reg_uart_rx_timeout1(uart_num) BIT(2)
    UART_TXDONE_MASK = BIT(3),   // reg_uart_rx_timeout1(uart_num) BIT(6)
    UART_ERR_IRQ_MASK = BIT(4),  // reg_uart_rx_timeout1(uart_num) BIT(7)
} uart_irq_mask_e;

/**
 *  @brief  Define UART IRQ BIT STATUS FOR GET
 */
typedef enum {
    UART_RX_ERR = BIT(7),
    UART_TXDONE = BIT(0),
    UART_TXBUF_IRQ_STATUS = BIT(1),
    UART_RXDONE = BIT(2),
    UART_RXBUF_IRQ_STATUS = BIT(3),
} uart_irq_status_get_e;

/**
 *  @brief  Define UART IRQ BIT STATUS FOR CLR
 */
typedef enum {
    UART_CLR_RX = BIT(6),
    UART_CLR_TX = BIT(7),
} uart_irq_status_clr_e;

/**********************************************************************************************************************
 *                                     global variable declaration                                                    *
 *********************************************************************************************************************/

/**********************************************************************************************************************
 *                                      global function prototype                                                     *
 *********************************************************************************************************************/
/**
 * @brief     This function serves to get the rxfifo cnt.
 * @param[in] uart_num - UART0/UART1.
 * @return    none
 */
static inline unsigned char uart_get_rxfifo_num(uart_num_e uart_num)
{
    return reg_uart_buf_cnt(uart_num) & FLD_UART_RX_BUF_CNT;
}

/**
 * @brief     This function serves to get the txfifo cnt.
 * @param[in] uart_num - UART0/UART1.
 * @return    none
 */
static inline unsigned char uart_get_txfifo_num(uart_num_e uart_num)
{
    return (reg_uart_buf_cnt(uart_num) & FLD_UART_TX_BUF_CNT) >> 4;
}

/**
 * @brief     This function resets the UART module.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_reset(uart_num_e uart_num)
{
    reg_rst0 &= (~((uart_num) ? FLD_RST0_UART1 : FLD_RST0_UART0));
    reg_rst0 |= ((uart_num) ? FLD_RST0_UART1 : FLD_RST0_UART0);
}

/**
 * @brief     This function enable the clock of UART module.
 * @param[in] uart_num - UART0/UART1.
 * @return    none
 */
static inline void uart_clk_en(uart_num_e uart_num)
{
    reg_clk_en0 |= ((uart_num) ? FLD_CLK0_UART1_EN : FLD_CLK0_UART0_EN);
}

/**
 * @brief      This function initializes the UART module.
 * @param[in]  uart_num    - UART0 or UART1.
 * @param[in]  div         - uart clock divider.
 * @param[in]  bwpc        - bitwidth, should be set to larger than 2.
 * @param[in]  parity      - selected parity type for UART interface.
 * @param[in]  stop_bit    - selected length of stop bit for UART interface.
 * @return     none
 * @note 	   sys_clk      baudrate   g_uart_div         g_bwpc
 *
 *  	       16Mhz        9600          118   			 13
 *                          19200         118     			  6
 *          	            115200          9       		 13
 *
 * 	           24Mhz        9600          249       		  9
 *           	 	    	19200		  124                 9
 *          	 	    	115200         12    			 15
 *
 *   	       32Mhz        9600          235       		 13
 *          	 	        19200		  235                 6
 *           	 	 	    115200         17    			 13
 *
 *   	       48Mhz        9600          499       		  9
 *          	 	 	    19200		  249                 9
 *           	 	 	    115200         25    			 15
*/
extern void uart_init(uart_num_e uart_num, unsigned short div, unsigned char bwpc, uart_parity_e parity,
                      uart_stop_bit_e stop_bit);

/***********************************************************
 * @brief  		This function serves to calculate the best bwpc(bit width) .i.e reg0x96.
 * @param[in]	baudrate - baut rate of UART.
 * @param[in]	sysclk   - system clock.
 * @param[out]	div      - uart clock divider.
 * @param[out]	bwpc     - bitwidth, should be set to larger than 2.
 * @return 		none
 * @note        BaudRate*(div+1)*(bwpc+1) = system clock.
 *  		    simplify the expression: div*bwpc =  constant(z).
 * 		        bwpc range from 3 to 15.so loop and get the minimum one decimal point.
 */
void uart_cal_div_and_bwpc(unsigned int baudrate, unsigned int sysclk, unsigned short *div, unsigned char *bwpc);

/**
 * @brief  		This funtion serves to set r_rxtimeout. this setting is transfer one bytes need cycles base on uart_clk.
 * 				For example, if transfer one bytes (1start bit+8bits data+1 priority bit+2stop bits) total 12 bits,
 * 				this register setting should be (bpwc+1)*12.
 * @param[in]	uart_num - UART0 or UART1.
 * @param[in]	bwpc     - bitwidth, should be set to larger than 2.
 * @param[in]	bit_cnt  - bit number.
 * @param[in]	mul	     - mul.
 * @return 		none
 */
void uart_set_dma_rx_timeout(uart_num_e uart_num, unsigned char bwpc, unsigned char bit_cnt, uart_timeout_mul_e mul);

/**
 * @brief     This function serves to config the number level setting the irq bit of status register.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] rx_level - receive level value. ie 0x140089[0,3].
 * @return    none
 */
static inline void uart_rx_irq_trig_level(uart_num_e uart_num, unsigned char rx_level)
{
    reg_uart_ctrl3(uart_num) = (reg_uart_ctrl3(uart_num) & (~FLD_UART_RX_IRQ_TRIQ_LEV)) | (rx_level & 0x0f);
}

/**
 * @brief     This function serves to config the number level setting the irq bit of status register.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] tx_level - transmit level value.ie 0x140089[4,7].
 * @return    none
 */
static inline void uart_tx_irq_trig_level(uart_num_e uart_num, unsigned char tx_level)
{
    reg_uart_ctrl3(uart_num) = (reg_uart_ctrl3(uart_num) & (~FLD_UART_TX_IRQ_TRIQ_LEV)) | (tx_level << 4);
}

/**
 * @brief     This function serves to send data by byte with not DMA method.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] tx_data  - the data to be send.
 * @return    none
 */
void uart_send_byte(uart_num_e uart_num, unsigned char tx_data);

/**
 * @brief     This function serves to receive uart data by byte with not DMA method.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
unsigned char uart_read_byte(uart_num_e uart_num);
/**
 * @brief     This function serves to judge if the transmission of uart is done.
 * @param[in] uart_num - UART0 or UART1.
 * @return    return the tx status
 * -          0:tx is done     1:tx isn't done
 */
unsigned char uart_tx_is_busy(uart_num_e uart_num);
/**
 * @brief     This function serves to send uart0 data by halfword with not DMA method.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] data  - the data to be send.
 * @return    none
 */
void uart_send_hword(uart_num_e uart_num, unsigned short data);

/**
 * @brief     This function serves to send uart0 data by word with not DMA method.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] data - the data to be send.
 * @return    none
 */
void uart_send_word(uart_num_e uart_num, unsigned int data);

/**
 * @brief     This function sets the RTS pin's level manually.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] polarity - set the output of RTS pin(only for manual mode).
 * @return    none
 */
void uart_set_rts_level(uart_num_e uart_num, unsigned char polarity);

/**
 *	@brief		This function serves to set pin for UART0 cts function .
 *	@param[in]  cts_pin -To set cts pin.
 *	@return		none
 */
void uart_set_cts_pin(uart_cts_pin_e cts_pin);

/**
 *	@brief		This function serves to set pin for UART0 rts function .
 *	@param[in]  rts_pin - To set rts pin.
 *	@return		none
 */
void uart_set_rts_pin(uart_rts_pin_e rts_pin);

/**
* @brief      This function serves to select pin for UART module.
* @param[in]  tx_pin   - the pin to send data.
* @param[in]  rx_pin   - the pin to receive data.
* @return     none
*/
void uart_set_pin(uart_tx_pin_e tx_pin, uart_rx_pin_e rx_pin);

/**
* @brief      This function serves to select pin for UART module.
* @param[in]  rx_pin  - the pin serves to send and receive data.
* @return     none
*/
void uart_set_rtx_pin(uart_rx_pin_e rx_pin);

/**
 * @brief     	This function serves to send data by DMA, this function tell the DMA to get data from the RAM and start.
 * @param[in]  	uart_num - UART0 or UART1.
 * @param[in] 	addr     - pointer to the buffer containing data need to send.
 * @param[in] 	len      - DMA transmission length.The maximum transmission length of DMA is 0xFFFFFC bytes,
 *                         so dont'n over this length.
 * @return      1  dma start send.
 *              0  the length is error.
 */
unsigned char uart_send_dma(uart_num_e uart_num, unsigned char *addr, unsigned int len);

/**
* @brief     This function serves to send data with not DMA method.
* @param[in] uart_num - UART0 or UART1.
* @param[in] addr     - pointer to the buffer containing data need to send.
* @param[in] len      - NDMA transmission length.
* @return    1
*/
unsigned char uart_send(uart_num_e uart_num, unsigned char *addr, unsigned char len);

/**
 * @brief     	This function serves to receive data function by DMA,
 *              this  function tell the DMA to get data from the uart data fifo.
 * @param[in]  	uart_num - UART0 or UART1.
 * @param[in] 	addr     - pointer to the buffer  receive data.
 * @param[in]   rev_size - the receive length of DMA.The maximum transmission length of DMA is 0xFFFFFC bytes,
 *                         so dont'n over this length.
 * @note        The DMA version of A0  has some limitians.
 *              1:The receive length should be greater or equal to the data you want to receive,
 *              then the data won't be lost.
 *              2:You have to estimate the data-length that you want to receive.
 *              If the data length you set isn't the multiple of 4(the DMA carry 4-byte one time),like 5,
 *              it will carry 8 byte,while the last 3-byte data is random.
 *              The DMA version of A1 can receive any length of data,the rev_size is useless.
 * @return    	none
 */
extern void uart_receive_dma(uart_num_e uart_num, unsigned char *addr, unsigned int rev_size);

/**
  * @brief     This function serves to set uart tx_dam channel and config dma tx default.
  * @param[in] uart_num - UART0 or UART1.
  * @param[in] chn      - dma channel.
  * @return    none
  */
extern void uart_set_tx_dma_config(uart_num_e uart_num, dma_chn_e chn);

/**
  * @brief     This function serves to set uart rx_dam channel and config dma rx default.
  * @param[in] uart_num - UART0 or UART1.
  * @param[in] chn      - dma channel.
  * @return    none
  */
extern void uart_set_rx_dma_config(uart_num_e uart_num, dma_chn_e chn);

/**
 * @brief     This function serves to config the irq of uart tx and rx.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] mask     - uart irq mask.
 * @return    none
 */
static inline void uart_set_irq_mask(uart_num_e uart_num, uart_irq_mask_e mask)
{
    if ((mask & UART_RX_IRQ_MASK) || (mask & UART_TX_IRQ_MASK)) {
        reg_uart_ctrl0(uart_num) |=
            (((mask & UART_RX_IRQ_MASK) ? 1 : 0) << 6) | (((mask & UART_TX_IRQ_MASK) ? 1 : 0) << 7);
    }

    if ((mask & UART_RXDONE_MASK) || (mask & UART_TXDONE_MASK) || (mask & UART_ERR_IRQ_MASK)) {
        reg_uart_rx_timeout1(uart_num) |= (((mask & UART_RXDONE_MASK) ? 1 : 0) << 2) |
                                          (((mask & UART_TXDONE_MASK) ? 1 : 0) << 6) |
                                          (((mask & UART_ERR_IRQ_MASK) ? 1 : 0) << 7);
    }
}

/**
 * @brief     This function serves to clear the irq of uart tx and rx.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] mask     - uart irq mask.
 * @return    none
 */
static inline void uart_clr_irq_mask(uart_num_e uart_num, uart_irq_mask_e mask)
{
    if ((mask & UART_RX_IRQ_MASK) || (mask & UART_TX_IRQ_MASK)) {
        reg_uart_ctrl0(uart_num) &=
            ~((((mask & UART_RX_IRQ_MASK) ? 1 : 0) << 6) | (((mask & UART_TX_IRQ_MASK) ? 1 : 0) << 7));
    }
    if ((mask & UART_RXDONE_MASK) || (mask & UART_TXDONE_MASK) || (mask & UART_ERR_IRQ_MASK)) {
        reg_uart_rx_timeout1(uart_num) &=
            ~((((mask & UART_RXDONE_MASK) ? 1 : 0) << 2) | (((mask & UART_TXDONE_MASK) ? 1 : 0) << 6) |
              (((mask & UART_ERR_IRQ_MASK) ? 1 : 0) << 7));
    }
}

/**
 * @brief     This function serves to get the irq status of uart tx and rx.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] status   - uart irq mask.
 * @return    irq status
 */
static inline unsigned int uart_get_irq_status(uart_num_e uart_num, uart_irq_status_get_e status)
{
    if (status == UART_RX_ERR) {
        return (reg_uart_status1(uart_num) & (status));
    } else {
        return (reg_uart_status2(uart_num) & (status));
    }
}

/**
 * @brief     This function serves to clear the irq status of uart tx and rx.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] status - uart irq mask.
 * @return    none
 */
static inline void uart_clr_irq_status(uart_num_e uart_num, uart_irq_status_clr_e status)
{
    reg_uart_status1(uart_num) |= (status);
}

/**
 * @brief     This function serves to set uart rts enable.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_set_rts_en(uart_num_e uart_num)
{
    reg_uart_ctrl2(uart_num) |= FLD_UART_RTS_EN;  // enable RTS function
}

/**
 * @brief     This function serves to set uart rts disable.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_set_rts_dis(uart_num_e uart_num)
{
    reg_uart_ctrl2(uart_num) &= (~FLD_UART_RTS_EN);  // disable RTS function
}

/**
 * @brief     This function serves to set uart cts enable.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_set_cts_en(uart_num_e uart_num)
{
    reg_uart_ctrl1(uart_num) |= FLD_UART_TX_CTS_ENABLE;  // enable CTS function
}

/**
 * @brief     This function serves to set uart cts disable.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_set_cts_dis(uart_num_e uart_num)
{
    reg_uart_ctrl1(uart_num) &= (~FLD_UART_TX_CTS_ENABLE);  // disable CTS function
}

/**
  * @brief     This function serves to configure UART hardware flow. Configure CTS.
  * @param[in] uart_num   - UART0 or UART1.
  * @param[in] cts_pin    - RTS pin select.
  * @param[in] cts_parity - when CTS's input equals to select, tx will be stopped.
  * @return    none
  */
extern void uart_cts_config(uart_num_e uart_num, uart_cts_pin_e cts_pin, unsigned char cts_parity);

/**
 * @brief     This function serves to configure UART hardware flow. Configure RTS.
 * @param[in] uart_num     - UART0 or UART1.
 * @param[in] rts_pin      - RTS pin select.
 * @param[in] rts_parity   - whether invert the output of RTS pin(only for auto mode).
 * @param[in] auto_mode_en - set the mode of RTS(auto or manual).
 * @return    none
 */
extern void uart_rts_config(uart_num_e uart_num, uart_rts_pin_e rts_pin, unsigned char rts_parity,
                            unsigned char auto_mode_en);

/**
 * @brief     This function serves to set uart rts trig lexel in auto mode.
 * @param[in] uart_num - UART0 or UART1.
 * @param[in] level    - threshold of trig RTS pin's level toggle(only for auto mode).
 * @return    none
 */
static inline void uart_rts_trig_level_auto_mode(uart_num_e uart_num, unsigned char level)
{
    reg_uart_ctrl2(uart_num) &= (~FLD_UART_RTS_TRIQ_LEV);
    reg_uart_ctrl2(uart_num) |= (level & FLD_UART_RTS_TRIQ_LEV);
}

/**
 * @brief     This function serves to set uart rts auto mode.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_rts_auto_mode(uart_num_e uart_num)
{
    reg_uart_ctrl2(uart_num) &= (~FLD_UART_RTS_MANUAL_M);
}

/**
 * @brief     This function serves to set uart rts manual mode.
 * @param[in] uart_num - UART0 or UART1.
 * @return    none
 */
static inline void uart_rts_manual_mode(uart_num_e uart_num)
{
    reg_uart_ctrl2(uart_num) |= (FLD_UART_RTS_MANUAL_M);
}

/**
 * @brief     This function is used to set the 'uart_rx_byte_index' to 0.
 *			  after wakeup from power-saving mode or reset uart, you must call this function before receiving the data.
 * @param[in] uart_num
 * @return    none.
 */
static inline void uart_clr_rx_index(uart_num_e uart_num)
{
    uart_rx_byte_index[uart_num] = 0;
}

/**
 * @brief     This function is used to set the 'uart_tx_byte_index' to 0.
 *			  after wakeup from power-saving mode or reset uart, you must call this function before sending the data.
 * @param[in] uart_num
 * @return    none.
 */
static inline void uart_clr_tx_index(uart_num_e uart_num)
{
    uart_tx_byte_index[uart_num] = 0;
}

/**
 * @brief     This function is used to clr uart tx-done,which means set tx-done to 0
 * @param[in] uart_num
 * @return    none.
 */
static inline void uart_clr_tx_done(uart_num_e uart_num)
{
    reg_uart_state(uart_num) |= BIT(7);
}

/**
 * @brief      	This function is used to enable the rtx function of .
 * @param[in]  	chn - UART0 or UART1.
 * @return     	none.
 */
static inline void uart_rtx_en(uart_num_e chn)
{
    reg_uart_rx_timeout1(chn) |= FLD_UART_P7816_EN;
}

#endif /* UART_H_ */
